using System.Collections.Generic;
using Godot;

namespace MyCraft2;

public partial class Chunk : MeshInstance3D
{
    public static readonly ShaderMaterial ShaderMaterial;

    static Chunk()
    {
        ShaderMaterial = new ShaderMaterial
        {
            Shader = ResourceLoader.Load<Shader>("res://shaders/chunk_material.gdshader")
        };
    }

    private readonly Dictionary<int, ChunkTier> Tiers = [];
    private readonly ArrayMesh ArrayMesh = new();

    public Chunk()
    {
        Mesh = ArrayMesh;
        MaterialOverride = ShaderMaterial;
    }

    public void SetBlock(Vector3I position, Block? block, bool commit = false, bool commitAdjacentChunkTier = false)
    {
        if (position.Y < 0) return;
        var layer = position.Y / 16;
        if (!Tiers.TryGetValue(layer, out var tier))
        {
            tier = new ChunkTier(ArrayMesh);
            Tiers[layer] = tier;
        }

        tier.SetBlock(position, block, commit);
        if (commit)
        {
            foreach (var child in GetChildren())
                if (child is StaticBody3D staticBody3D)
                {
                    RemoveChild(staticBody3D);
                    staticBody3D.QueueFree();
                }

            CreateTrimeshCollision();
        }

        if (commitAdjacentChunkTier)
        {
            if (Tiers.TryGetValue(layer + 1, out var tierUp)) tierUp.Commit();
            if (Tiers.TryGetValue(layer - 1, out var tierDown)) tierDown.Commit();
        }
    }

    public Block? GetBlock(Vector3I position)
    {
        var layer = position.Y / 16;
        if (layer < 0 || !Tiers.TryGetValue(layer, out var tier)) return null;
        return tier.GetBlock(position);
    }

    public void Commit(int y = -1)
    {
        if (y >= 0)
        {
            GetTier(y)?.Commit();
        }
        else
        {
            foreach (var tier in Tiers.Values)
                tier.Commit();
            foreach (var child in GetChildren())
                if (child is StaticBody3D staticBody3D)
                    staticBody3D.QueueFree();
        }

        CreateTrimeshCollision();
    }

    private ChunkTier? GetTier(int y)
    {
        var layer = y / 16;
        if (layer < 0 || !Tiers.TryGetValue(layer, out var tier)) return null;
        return tier;
    }
}